package email

import (
	"context"
	"errors"
	"fmt"
	"gitee.com/bobo-rs/creative-framework/pkg/sredis"
	"gitee.com/bobo-rs/creative-framework/pkg/utils"
	"gitee.com/bobo-rs/innovideo-services/consts"
	"gitee.com/bobo-rs/innovideo-services/library/tools"
	"github.com/gogf/gf/v2/container/gvar"
	"github.com/gogf/gf/v2/frame/g"
	"time"
)

// SendCaptcha 发送邮箱验证码
func (e *sEmail) SendCaptcha(ctx context.Context, email, subject, message, code string) error {
	// 校验发送规则
	err := e.Validator(ctx, email)
	if err != nil {
		return err
	}

	// 实例邮件服务
	service, err := e.MailService(ctx)
	if err != nil {
		return err
	}
	// 发送消息
	err = service.SendMail(subject, message, email)
	if err != nil {
		return err
	}

	// 缓存验证码
	if _, err = e.GetOrSet(ctx, e.GetCaptchaPrefix(consts.EmailCaptchaSendCode)+email, code, true, time.Minute*5); err != nil {
		return fmt.Errorf(`record failed to send verification code, %w`, err)
	}

	// 记录验证规则
	_ = e.CacheCaptchaRule(ctx, email)
	return nil
}

// ValidatorCaptcha 验证邮件发送验证码
func (e *sEmail) ValidatorCaptcha(ctx context.Context, email, code string) error {
	// 检测验证码是否存在
	r, err := e.GetOrSet(
		ctx, e.GetCaptchaPrefix(consts.EmailCaptchaSendCode)+email, ``, false, 0,
	)
	if err != nil {
		return err
	}
	// 是否存在
	if r == nil {
		return errors.New(`please send verification`)
	}

	// 验证验证码
	if r.String() != code {
		return errors.New(`verification failed code not match`)
	}

	// 移除验证码缓存，避免盗用
	_ = e.RemoveCaptchaCache(ctx, email)
	return nil
}

// Validator 校验邮箱验证安全规则
func (e *sEmail) Validator(ctx context.Context, email string) error {
	// 获取配置
	config, err := e.Config(ctx)
	if err != nil {
		return err
	}
	// 规定时间内不允许重复发送（等于0无需检验）
	expire, err := e.GetOrSetCaptcha1Minute(ctx, email, false)
	if err != nil {
		return err
	}
	// 在规定效期内不允许重发
	if expire > 0 {
		return errors.New(`the email has been sent, please do not repeat it`)
	}
	// 同一个IP1小时发送量
	if ipLimitNum := config.CaptchaSwitchIpHourThreshold; ipLimitNum > 0 {
		ipSendNum, err := e.GetOrSetCaptcha1Hour(ctx, false)
		if err != nil {
			return err
		}
		// IP发送量已超阈值
		if ipSendNum >= ipLimitNum {
			return errors.New(`the current IP transmission volume has exceeded the threshold`)
		}
	}
	// 同一个邮箱一天发送量
	if eToday := config.CaptchaSwitchTodayThreshold; eToday > 0 {
		eTodayNum, err := e.GetOrSetCaptchaEmailTodayCount(ctx, email, false)
		if err != nil {
			return err
		}
		// 已超阈值
		if eTodayNum >= eToday {
			return fmt.Errorf(`the current email transmission volume has exceeded the threshold %d`, eTodayNum)
		}
	}
	// 当天发送量
	if todayTotal := config.CaptchaSwitchTodayTotal; todayTotal > 0 {
		sendTotal, err := e.GetOrSetCaptchaTodayTotal(ctx, false)
		if err != nil {
			return err
		}
		// 已超阈值
		if sendTotal >= todayTotal {
			return errors.New(`the total number of emails sent has exceeded the threshold`)
		}
	}
	return nil
}

// CacheCaptchaRule 缓存验证码规则
func (e *sEmail) CacheCaptchaRule(ctx context.Context, email string) (err error) {
	defer func() {
		if err != nil {
			g.Log().Debug(ctx, `缓存邮件验证码失败`, err)
		}
	}()
	// 记录邮箱发送量
	_, err = e.GetOrSetCaptcha1Minute(ctx, email, true)
	// 记录IP一小时发送量
	_, err = e.GetOrSetCaptcha1Hour(ctx, true)
	// 记录邮箱一天发送量
	_, err = e.GetOrSetCaptchaEmailTodayCount(ctx, email, true)
	// 记录一天邮件发送量
	_, err = e.GetOrSetCaptchaTodayTotal(ctx, true)
	return err
}

// RemoveCaptchaCache 移除验证码缓存规则
func (e *sEmail) RemoveCaptchaCache(ctx context.Context, email string) error {
	// 移除验证码和锁定规则
	return sredis.NewCache().Removes(ctx, []interface{}{
		e.GetCaptchaPrefix(consts.EmailCaptchaSendEmail1minuteLock) + email,
		e.GetCaptchaPrefix(consts.EmailCaptchaSendCode) + email,
	})
}

// GetOrSetCaptcha1Minute 获取并设置验证码已发时间
func (e *sEmail) GetOrSetCaptcha1Minute(ctx context.Context, email string, t bool) (int, error) {
	// 获取KEY
	cacheKey := e.GetCaptchaPrefix(consts.EmailCaptchaSendEmail1minuteLock) + email
	r, err := e.GetOrSet(ctx, cacheKey, time.Now().Unix(), t, time.Minute)
	if err != nil {
		return 0, err
	}
	return r.Int(), nil
}

// GetOrSetCaptcha1Hour 获取或设置同一个IP1小时发送总量
func (e *sEmail) GetOrSetCaptcha1Hour(ctx context.Context, t bool) (int, error) {
	// 获取KEY
	cacheKey := e.GetCaptchaPrefix(consts.EmailCaptchaSendIp1HourLock) + utils.GetIp(ctx)
	return e.GetOrSetIncr(ctx, cacheKey, t, time.Hour)
}

// GetOrSetCaptchaEmailTodayCount 获取或设置今日同一个邮箱发送总量
func (e *sEmail) GetOrSetCaptchaEmailTodayCount(ctx context.Context, email string, t bool) (int, error) {
	cacheKey := e.GetCaptchaPrefix(consts.EmailCaptchaSendTodayCountLock) + email
	return e.GetOrSetIncr(ctx, cacheKey, t, time.Hour*24)
}

// GetOrSetCaptchaTodayTotal 获取或设置今日邮件发送总量
func (e *sEmail) GetOrSetCaptchaTodayTotal(ctx context.Context, t bool) (int, error) {
	cacheKey := e.GetCaptchaPrefix(consts.EmailCaptchaSendTodayTotalLock)
	return e.GetOrSetIncr(ctx, cacheKey, t, time.Hour*24)
}

// GetOrSetIncr 获取并设置叠加数据
func (e *sEmail) GetOrSetIncr(ctx context.Context, key string, t bool, expire time.Duration) (int, error) {
	r, err := sredis.New().Get(ctx, key)
	if err != nil {
		return 0, err
	}
	// 设置
	if t {
		err = sredis.NewRedis().IncrEx(ctx, key, expire)
		return 0, err
	}
	return r.Int(), nil
}

// GetOrSet 获取并设置缓存
func (e *sEmail) GetOrSet(ctx context.Context, key string, value interface{}, t bool, expire time.Duration) (*gvar.Var, error) {
	r, err := sredis.NewCache().Get(ctx, key)
	if err != nil {
		return nil, err
	}
	// 是否设置
	if t {
		err = sredis.NewCache().Set(ctx, key, value, expire)
		return nil, err
	}
	return r, nil
}

// GetCaptchaPrefix 获取短信验证缓存KEY前缀
func (e sEmail) GetCaptchaPrefix(suffix string) string {
	return tools.CacheKey(consts.EmailCaptchaPrefix, suffix)
}
